package http

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"smart-flow/monitor/config"
	"smart-flow/monitor/logger"
	"smart-flow/monitor/proto"
	"smart-flow/monitor/service"
	"time"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

type ServerManager struct {
	server *http.Server
}

func (m *ServerManager) Start() bool {
	return m.startManageServer()
}

func (m *ServerManager) startManageServer() bool {
	gin.SetMode(config.AppConfig().Monitor.Mode)
	r := gin.New()
	r.Use(gin.Logger(), gin.Recovery())
	r.Static(config.PathDownload, "./download")
	r.GET(config.PathAgentInfo, AgentInfoHandle)
	r.POST(config.PathAgentInfo, AgentInfoHandle)
	r.POST(config.PathAgentLogin, AgentLoginHandle)
	r.POST(config.PathAgentAlive, AgentAliveHandle)
	r.GET(config.PathPusherInfo, PusherInfoHandle)
	r.POST(config.PathPusherInfo, PusherInfoHandle)
	r.POST(config.PathPusherLogin, PusherLoginHandle)
	r.POST(config.PathPusherAlive, PusherAliveHandle)
	listener, err := net.Listen("tcp", fmt.Sprintf(":%d", config.HttpManagePort))
	if err != nil {
		logger.Logger.Error("[管控] http 服务监听错误", zap.Int("port", config.HttpManagePort), zap.Error(err))
		return false
	}
	m.server = &http.Server{Handler: r}
	errChan := make(chan error, 1)
	go func() {
		errChan <- m.server.Serve(listener)
	}()
	select {
	case <-errChan:
		return false
	case <-time.After(time.Second):
		logger.Logger.Info("[管控] http 服务启动成功", zap.Int("port", config.HttpManagePort))
		return true
	}
}

func (m *ServerManager) Stop() {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	_ = m.server.Shutdown(ctx)
}

func AgentInfoHandle(c *gin.Context) {
	if c.Request.Method == http.MethodGet {
		c.String(http.StatusOK, "protocol_ok")
		return
	}
	var req proto.InfoReq
	err := c.ShouldBindJSON(&req)
	if err != nil {
		logger.Logger.Warn("[管控] AgentInfoHandle Agent协议不正确...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	if req.AgentVer < config.AgentMinVer {
		logger.Logger.Warn("[管控] AgentInfoHandle Agent版本太低了...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	logger.Logger.Info("[管控] AgentInfoHandle Agent获取信息...", zap.Any("data", req))
	c.JSON(http.StatusOK, proto.InfoResp{
		Timestamp:     time.Now().Unix(),
		LoginUrl:      config.EndpointsInfo.LoginUrl,
		AliveUrl:      config.EndpointsInfo.AliveUrl,
		ProveV6Url:    config.EndpointsInfo.ProveV6Url,
		SpeedUrl:      service.RandomSpeed(req.AgentV6Reachable),
		SpeedDuration: config.SpeedDuration,
	})
}

func AgentLoginHandle(c *gin.Context) {
	var req proto.LoginReq
	err := c.ShouldBindJSON(&req)
	if err != nil {
		logger.Logger.Warn("[管控] AgentLoginHandle Agent协议不正确...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	if req.AgentVer < config.AgentMinVer {
		logger.Logger.Warn("[管控] AgentLoginHandle Agent版本太低了...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	tcpRateLimit, udpRateLimit, httpRateLimit, perUseCount := service.CalcRateLimit(req.Bandwidth, req.AgentRateLimit)
	tcpAddresses, udpAddresses, httpUrls := service.RandomAddress(req.AgentV6Reachable)
	if config.AppConfig().Dy.TcpProportion > 0 {
		if tcpRateLimit == 0 || len(tcpAddresses) == 0 {
			logger.Logger.Warn("[管控] AgentLoginHandle 无可用Tcp Container...", zap.Any("data", req))
			c.String(http.StatusBadRequest, "bad request")
			return
		}
	}
	if config.AppConfig().Dy.UdpProportion > 0 {
		if udpRateLimit == 0 || len(udpAddresses) == 0 {
			logger.Logger.Warn("[管控] AgentLoginHandle 无可用Udp Container...", zap.Any("data", req))
			c.String(http.StatusBadRequest, "bad request")
			return
		}
	}
	if config.AppConfig().Dy.HttpProportion > 0 {
		if httpRateLimit == 0 || len(httpUrls) == 0 {
			logger.Logger.Warn("[管控] AgentLoginHandle 无可用Http Container...", zap.Any("data", req))
			c.String(http.StatusBadRequest, "bad request")
			return
		}
	}
	go func(info proto.AgentInfo) {
		uid := info.AgentId
		if info.AgentV6Reachable == 0 {
			service.SessionsInstance().AddV4Session(uid, tcpRateLimit+udpRateLimit+httpRateLimit)
		} else {
			service.SessionsInstance().AddV6Session(uid, tcpRateLimit+udpRateLimit+httpRateLimit)
		}
	}(req.AgentInfo)
	logger.Logger.Info("[管控] AgentLoginHandle Agent开始运行了...",
		zap.Any("data", req), zap.Int64("tcpRateLimit", tcpRateLimit),
		zap.Int64("udpRateLimit", udpRateLimit),
		zap.Int64("httpRateLimit", httpRateLimit),
		zap.Int("perUseCount", perUseCount),
		zap.String("clientIp", c.ClientIP()),
		zap.String("remoteIp", c.RemoteIP()),
	)
	c.JSON(http.StatusOK, proto.LoginResp{
		Timestamp:     time.Now().Unix(),
		AgentInterval: config.AppConfig().Dy.AgentInterval,
		BlockCount:    service.RandomBlockCount(),
		TcpRateLimit:  tcpRateLimit,
		UdpRateLimit:  udpRateLimit,
		HttpRateLimit: httpRateLimit,
		RateDelta:     config.AppConfig().Dy.DeltaProportion,
		PerUseCount:   perUseCount,
		TcpAddresses:  tcpAddresses,
		UdpAddresses:  udpAddresses,
		HttpUrls:      httpUrls,
	})
}

func AgentAliveHandle(c *gin.Context) {
	var req proto.AliveReq
	err := c.ShouldBindJSON(&req)
	if err != nil {
		logger.Logger.Warn("[管控] AgentAliveHandle Agent协议不正确...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	if req.AgentVer < config.AgentMinVer {
		logger.Logger.Warn("[管控] AgentAliveHandle Agent版本太低了...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	now := time.Now().Unix()
	if now >= req.EndTime {
		logger.Logger.Warn("[管控] AgentAliveHandle Agent运行太久了...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	uid := req.AgentId
	if req.AgentV6Reachable == 0 {
		if !service.SessionsInstance().ExistV4Session(uid) {
			logger.Logger.Warn("[管控] AgentAliveHandle Agent中途休眠了...", zap.Any("data", req))
			c.String(http.StatusBadRequest, "bad request")
			return
		}
	} else {
		if !service.SessionsInstance().ExistV6Session(uid) {
			logger.Logger.Warn("[管控] AgentAliveHandle Agent中途休眠了...", zap.Any("data", req))
			c.String(http.StatusBadRequest, "bad request")
			return
		}
	}
	go func(info proto.AgentInfo) {
		if info.AgentV6Reachable == 0 {
			service.SessionsInstance().UpdateV4Session(uid)
		} else {
			service.SessionsInstance().UpdateV6Session(uid)
		}
	}(req.AgentInfo)
	// logger.Logger.Info("[管控] AgentAliveHandle Agent运行中...", zap.Any("data", req))
	c.JSON(http.StatusOK, proto.AliveResp{
		Timestamp:      now,
		NeedUpdateRate: 0,
		TcpRateLimit:   0,
		UdpRateLimit:   0,
		HttpRateLimit:  0,
		PerUseCount:    0,
	})
}

func PusherInfoHandle(c *gin.Context) {
	if c.Request.Method == http.MethodGet {
		c.String(http.StatusOK, "protocol_ok")
		return
	}
	var req proto.PusherInfoReq
	err := c.ShouldBindJSON(&req)
	if err != nil {
		logger.Logger.Warn("[管控] PusherInfoHandle Pusher协议不正确...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	if req.PusherVer < config.PusherMinVer {
		logger.Logger.Warn("[管控] PusherInfoHandle Pusher版本太低了...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	logger.Logger.Info("[管控] PusherInfoHandle Pusher获取信息...", zap.Any("data", req))
	c.JSON(http.StatusOK, proto.PusherInfoResp{
		Timestamp:     time.Now().Unix(),
		LoginUrl:      config.PusherEndpointsInfo.LoginUrl,
		AliveUrl:      config.PusherEndpointsInfo.AliveUrl,
		SpeedUrl:      service.RandomSpeed(0),
		SpeedDuration: config.SpeedDuration,
		BeRunning:     service.CalcBeRunning(req.PusherDuration),
	})
}

func PusherLoginHandle(c *gin.Context) {
	var req proto.PusherLoginReq
	err := c.ShouldBindJSON(&req)
	if err != nil {
		logger.Logger.Warn("[管控] PusherInfoHandle Pusher协议不正确...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	if req.PusherVer < config.PusherMinVer {
		logger.Logger.Warn("[管控] PusherInfoHandle Pusher版本太低了...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	_, udpRateLimit, _, perUseCount := service.CalcRateLimit(req.Bandwidth, req.PusherRateLimit)
	if config.AppConfig().Dy.UdpProportion < 1 {
		logger.Logger.Warn("[管控] PusherLoginHandle 无可用Udp Container...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	if config.AppConfig().Dy.PusherStrictRate > 0 && udpRateLimit < req.PusherRateLimit {
		logger.Logger.Warn("[管控] PusherLoginHandle Pusher带宽不足了...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	logger.Logger.Info("[管控] PusherLoginHandle Pusher开始运行了...",
		zap.Any("data", req),
		zap.Int64("udpRateLimit", udpRateLimit),
		zap.Int("perUseCount", perUseCount),
		zap.String("clientIp", c.ClientIP()),
		zap.String("remoteIp", c.RemoteIP()),
	)
	c.JSON(http.StatusOK, proto.PusherLoginResp{
		Timestamp:      time.Now().Unix(),
		PusherInterval: config.AppConfig().Dy.PusherInterval,
		StatInterval:   config.AppConfig().Dy.StatInterval,
		BlockCount:     service.RandomBlockCount(),
		UdpRateLimit:   udpRateLimit,
		RateDelta:      config.AppConfig().Dy.DeltaProportion,
		PerUseCount:    perUseCount,
		StatAddr:       service.RandomStat(),
	})
}

func PusherAliveHandle(c *gin.Context) {
	var req proto.PusherAliveReq
	err := c.ShouldBindJSON(&req)
	if err != nil {
		logger.Logger.Warn("[管控] PusherAliveHandle Pusher协议不正确...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	if req.PusherVer < config.PusherMinVer {
		logger.Logger.Warn("[管控] PusherAliveHandle Pusher版本太低了...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	now := time.Now().Unix()
	if now >= req.EndTime {
		logger.Logger.Warn("[管控] PusherAliveHandle Pusher运行太久了...", zap.Any("data", req))
		c.String(http.StatusBadRequest, "bad request")
		return
	}
	uid := req.PusherId
	if req.PusherV6Reachable == 0 {
		if !service.SessionsInstance().ExistV4Session(uid) {
			logger.Logger.Warn("[管控] PusherAliveHandle Pusher中途休眠了...", zap.Any("data", req))
			c.String(http.StatusBadRequest, "bad request")
			return
		}
	} else {
		if !service.SessionsInstance().ExistV6Session(uid) {
			logger.Logger.Warn("[管控] PusherAliveHandle Pusher中途休眠了...", zap.Any("data", req))
			c.String(http.StatusBadRequest, "bad request")
			return
		}
	}
	// logger.Logger.Info("[管控] PusherAliveHandle Pusher运行中...", zap.Any("data", req))
	c.JSON(http.StatusOK, proto.PusherAliveResp{
		Timestamp: now,
	})
}
